package org.marlin.http.proxy;

import org.marlin.http.annotation.HttpClient;
import org.marlin.http.annotation.HttpPath;
import org.marlin.http.handler.HttpHandler;
import org.marlin.http.http.Uri;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * init version 1.5.0
 * @author Jack ZG
 * @since 1.0.0
 */
public class HttpProxyFactory<T> {
    private final Class<T> x5Interface;
    private final Class httpHandlerRef;
    private final Map<Method, Uri> methodCache = new ConcurrentHashMap<>();


    public HttpProxyFactory(Class<T> x5Interface) {
        this.x5Interface = x5Interface;
        HttpClient httpClient = getHttpClientAnnotation(x5Interface);
        this.httpHandlerRef = httpClient.httpHandlerRef();
        Method[] methods = x5Interface.getMethods();
        for (Method method : methods) {
            HttpPath[] httpPaths = method.<HttpPath>getAnnotationsByType(HttpPath.class);
            if (httpPaths.length == 0) {
                throw new IllegalStateException(x5Interface + "not has @HttpPath");
            }
            if (httpPaths.length > 1) {
                throw new IllegalStateException(x5Interface + "not duplicate @HttpPath");
            }
            HttpPath httpPath = httpPaths[0];
            Uri uri = new Uri(httpClient.rootPath() + httpPath.value(), httpClient.hostSpace(), httpPath.type(), httpPath.returnClass());
            uri.setMethod(httpPath.method());
            this.methodCache.put(method, uri);
        }
    }

    private HttpClient getHttpClientAnnotation(Class<T> x5Interface) {
        HttpClient[] httpClients = x5Interface.<HttpClient>getAnnotationsByType(HttpClient.class);
        if (httpClients.length == 0) {
            throw new IllegalStateException(x5Interface + "not has @HttpClient");
        }
        if (httpClients.length > 1) {
            throw new IllegalStateException(x5Interface + "not duplicate @HttpClient");
        }
        return httpClients[0];
    }

    public Class<T> getX5Interface() {
        return this.x5Interface;
    }

    public Map<Method, Uri> getMethodCache() {
        return this.methodCache;
    }


    protected T newInstance(Map<Class, HttpHandler> httpHandlers) {
        return (T) Proxy.newProxyInstance(this.x5Interface.getClassLoader(), new Class[]{this.x5Interface}, new HttpProxy(this.methodCache, httpHandlers.get(this.httpHandlerRef)));
    }
}

